App.require(["htmlTool"], function (htmlTool) {
    var app = App.getCurrent();

    app.defineComponent("str-menu-list", {
        render: function (next) {
            this.computedData();
            this.fetchAll(["./item.html", "./index.html"], function (textList) {
                this.data.itemHTML = textList[0];
                next(this.renderHTML(textList[1].replace("{{itemHTML}}", textList[0])));
            })
        },
        getDomObj: function () {
            this.attachDom('[role="menulist"]', "container")
                .attachEvent("container", "click", this.clickHandler, false)
                .attachEvent("container", "change", this.changeHandler, false);
        },
        reset: function () {
            if (this.property.multiple) {
                this.data.value = [];
                var checkboxes = this.domList.container.querySelectorAll("input");
                for (var i = 0; i < checkboxes.length; i++) {
                    checkboxes[i].checked = checkboxes[i].defaultChecked;
                    if (checkboxes[i].checked) {
                        this.data.value.push({
                            value: this.data.list[i].value,
                            content: this.data.list[i].content
                        })
                    }
                }
            }
        },
        changeHandler: function (ev) {
            var target = ev.target,
                div = target.nextElementSibling.lastElementChild;
            if (div && div.dataset.action) {
                var content = div.querySelector("span").textContent,
                    action = div.dataset.action;
                if (target.checked) {
                    this.data.value.push({
                        value: action,
                        content: content
                    });
                } else {
                    var index = this.getValueIndex(action);
                    if (index !== -1) this.data.value.splice(index, 1);
                }
                this.dispatchCustomEvent("menuitemchange", {
                    event: ev,
                    component: this,
                    value: this.data.value
                })
            }
        },
        getValueIndex: function (action) {
            var value = this.data.value;
            for (var i = 0; i < value.length; i++) {
                if (value[i].value === action) return i;
            }
            return -1;
        },
        clickHandler: function (ev) {
            var div = Component.getNodeName(ev.target, "DIV");
            if (div && div.dataset.action) {
                var content = div.querySelector("span").textContent,
                    action = div.dataset.action;

                if (!this.property.multiple) {
                    this.data.value = {
                        value: action,
                        content: content
                    };
                }
                this.dispatchCustomEvent("menuitemclick", {
                    component: this,
                    value: {value: action, content: content},
                    event: ev
                });
            }
        },
        computedData: function () {
            var property = this.property,
                data = this.data;
            data.uid = this.uid;
            property.class = property.class || "";
            data.list = htmlTool.getOptions(this.slot);
            data.value = "";
            property.multiple = typeof property.multiple !== "undefined" ? "multiple" : "";
            if (property.multiple) {
                data.value = [];
                for (var i = 0; i < data.list.length; i++) {
                    var obj = data.list[i];
                    if (obj.checked) data.value.push({
                        value: obj.value, content: obj.content
                    });
                }
            }
            this.data.propertystr = this.createPropertyStr({
                class: property.class
            });
        },
        _getOptionByValue: function (value, isIndex) {
            var list = this.data.list;
            for (var i = 0; i < list.length; i++) {
                if (list[i].value === value) {
                    if (isIndex) return i;
                    return list[i];
                }
            }
            if (isIndex) return -1;
            return false;
        },
        _getObj: function (value, content, property) {
            var result = {
                    uid: this.uid
                };
            for (var key in property) {
                result[key] = property[key];
            }
            result.value = value;
            result.content = content;
            return result;
        },
        appendOption: function (value, content, property) {
            if (this._getOptionByValue(value)) return false;
            var result = this._getObj(value, content, property);
            this.insertAdjacentHTML(this.domList.container, this.data.itemHTML, "beforeend", function () {
                that.data.list.push(result);
                if (that.property.multiple && "checked" in result) {
                    that.setChecked(value, true);
                }
            }, result);
        },
        insertOption: function (oldValue, newValue, content, property) {
            var that = this;
            var result = this._getObj(newValue, content, property);
            var index = this._getOptionByValue(oldValue, true);
            if (index > 0) {
                var menuItem = container.querySelectorAll(".str-menu-item")[index - 1];
                var target = this.property.multiple ? menuItem.parentNode : menuItem;
                this.insertAdjacentHTML(target, this.data.itemHTML, "afterend", function () {
                    that.splice(index - 1, result);
                    if (that.property.multiple && "checked" in result) {
                        that.setChecked(value, true);
                    }
                }, result)
            }
            else {
                this.insertAdjacentHTML(container, this.data.itemHTML, "afterbegin", function () {
                    that.data.list.unshift(result);
                    if (that.property.multiple && "checked" in result) {
                        that.setChecked(value, true);
                    }
                }, result)
            }
        },
        removeOption: function (value) {
            var menuItem = this.domList.container.querySelector("[data-action='" + value + "']");
            if (menuItem) {
                var components = this.components,
                    container = this.domList.container,
                    index = -1;
                for (var i = components.length - 1; i >= 0; i--) {
                    if (menuItem.contains(components[i].nodes[0])) this.removeComponent(components[i]);
                }

                if (this.property.multiple) {
                    var label = menuItem.parentNode,
                        input = label.previousElementSibling;
                    container.removeChild(label);
                    container.removeChild(input);
                    index = this.getValueIndex(value);
                    if (index !== -1) this.data.value.splice(index, 1);
                } else {
                    container.removeChild(menuItem);
                }
                index = this._getOptionByValue(value, true);
                if (index !== -1) this.data.list.splice(index, 1);
            }
        },
        setFocus: function (value) {
            var menuItem = this.domList.container.querySelector("[data-action='" + value + "']");
            if (menuItem) menuItem.focus();
        },
        setChecked: function (value, isBoolean) {
            if (this.property.multiple) {
                var input = this._getInputByValue(value);
                if (input.checked === !!isBoolean) return;
                input.checked = isBoolean;
                if (isBoolean) {
                    var option = this._getOptionByValue(value);
                    if (option) this.data.value.push(option);
                } else {
                    var index = this.getValueIndex(value);
                    if (index !== -1) this.data.value.splice(index, 1);
                }
            }
        },
        getCheckedValues: function () {
            if (this.property.multiple) {
                var list = this.data.list,
                    result = [];
                for (var i = 0; i < list.length; i++) {
                    var input = this._getInputByValue(list[i].value);
                    if (input && input.checked) result.push(list[i]);
                }
                return result;
            }
            return "";
        },
        _getInputByValue: function (value) {
            var id = this.uid + "-" + value;
            var input = this.domList.container.querySelector("#s" + id);
            return input;
        },
        getOptions: function () {
            return this.data.list;
        },
        prevFocus: function () {
            var container = this.domList.container,
                activeElement = document.activeElement,
                menuItems = container.querySelectorAll(".str-menu-item");

            if (container.contains(activeElement)) {
                for (var i = 0, len = menuItems.length; i < len; i++) {
                    if (menuItems[i] == activeElement) break;
                }
                if (i === 0) menuItems[len - 1].focus();
                else if (i === len) menuItems[0].focus();
                else menuItems[i - 1].focus();
            } else {
                menuItems[menuItems.length - 1].focus();
            }
        },
        nextFocus: function () {
            var container = this.domList.container,
                activeElement = document.activeElement,
                menuItems = container.querySelectorAll(".str-menu-item");

            if (container.contains(activeElement)) {
                for (var i = 0, len = menuItems.length; i < len; i++) {
                    if (menuItems[i] == activeElement) break;
                }
                if (i === len - 1) menuItems[0].focus();
                else if (i === len) menuItems[len - 1].focus();
                else menuItems[i + 1].focus();
            } else {
                menuItems[0].focus();
            }
        },
        setKeyboard: function () {
            this.keyEvent = this._keyEvent.bind(this);
            window.addEventListener("keydown", this.keyEvent, false);
        },
        removeKeyboard: function () {
            window.removeEventListener("keydown", this.keyEvent, false);
            this.keyEvent = null;
        },
        _keyEvent: function (ev) {
            if (["ArrowDown", "ArrowUp", "Enter", "Tab"].indexOf(ev.code) === -1) return;
            ev.preventDefault();
            if (ev.code === "ArrowDown" || ev.code === "Tab") {
                this.nextFocus();
            } else if (ev.code === "ArrowUp") {
                this.prevFocus();
            } else if (ev.code === "Enter") {
                var focusElement = document.activeElement;
                if (!this.domList.container.contains(focusElement)) return;
                var content = focusElement.querySelector("span").textContent,
                    value = focusElement.dataset.action;
                if (this.property.multiple) {
                    var input = focusElement.parentNode.previousElementSibling;
                    this.setChecked(value, !input.checked);
                    this.dispatchCustomEvent("menuitemchange", {
                        event: ev,
                        target: input,
                        component: this,
                        value: this.data.value
                    });
                } else {
                    this.dispatchCustomEvent("menuitemclick", {
                        component: this,
                        value: {
                            value: value,
                            content: content
                        },
                        event: ev
                    });
                }
            }
        },
        get multiple() {
            return this.property.multiple;
        },
        set multiple(value) {
            value = value ? "multiple" : "";
            if (this.property.multiple === value) return;
            var container = this.domList.container,
                menuItems = container.querySelectorAll(".str-menu-item");
            var fragment = document.createDocumentFragment();
            if (value) {
                for (var i = 0; i < menuItems.length; i++) {
                    var item = menuItems[i];
                    var action = item.dataset.action;
                    var input = document.createElement("input");
                    input.type = "checkbox";
                    input.tabIndex = -1;
                    var nameAndId = "s" + this.uid + "-" + action;
                    input.id = nameAndId;
                    input.name = nameAndId;
                    var label = document.createElement("label");
                    label.htmlFor = nameAndId;
                    label.innerHTML = [
                        '<i role="icon" class="str-icon input-icon">',
                            '<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24">',
                                '<path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path>',
                            '</svg>',
                        '</i>'
                    ].join("\n");
                    label.appendChild(item);
                    fragment.appendChild(input);
                    fragment.appendChild(label);
                }
                this.data.value = [];
            } else {
                container.innerHTML = "";
                for (var i = 0; i < menuItems.length; i++) {
                    fragment.appendChild(menuItems[i]);
                }
                this.data.value = "";
            }
            container.appendChild(fragment);
            this.property.multiple = value;
        },
        get className() {
            return this.property.class;
        },
        set className(value) {
            if (this.property.class === value) return;
            this.domList.container.className = value;
            this.property.class = value;
        }
    });
})